package com.iflytek.jzcpx.procuracy.ocr.common.util.pti.impl;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class GsCmdUtil
{
    private static Logger LOGGER = LoggerFactory.getLogger(GsCmdUtil.class);

    private final File gsExeFile;

    public GsCmdUtil(String path) throws IOException {
        File f = checkPath(path);
        if (f == null) {
            f = checkPath("/usr/bin/gs");
        }

        if (f == null) {
            f = checkPath("/usr/local/bin/gs");
        }
        if (f == null) {
            throw new IOException("GhostScript (gs) executable not found: " + path);
        }
        LOGGER.info("gs execute file abs path: {}", f.getCanonicalPath());

        this.gsExeFile = f;
    }

    public static File checkPath(String path) {
        if (StringUtils.isEmpty(path)) {
            return null;
        }
        File f = new File(path);
        return (f.exists() && f.isFile() && f.canExecute()) ? f : null;
    }

    private List<String> newGsArgs(String device, String pdfPassword, File outputFile) {
        List<String> args = new ArrayList<>();
        args.add(this.gsExeFile.getAbsolutePath());
        LOGGER.debug("gs command file path: {}", this.gsExeFile.getAbsolutePath());
        args.add("-dNOPAUSE");
        args.add("-dBATCH");
        args.add("-dSAFER");
        args.add("-sDEVICE=" + device);
        args.add("-dINTERPOLATE");
        args.add("-dNumRenderingThreads=" + Runtime.getRuntime().availableProcessors());
        args.add("-sPageList=1-");

        if (pdfPassword != null) {
            args.add("-sPDFPassword=" + pdfPassword);
        }

        args.add("-sOutputFile=" + outputFile.getAbsolutePath());
        return args;
    }

    private void execute(List<String> args, File pdfFile, StringWriter returnedText) throws IOException, InterruptedException {
        args.add(pdfFile.getAbsolutePath());
        run(args, 180, returnedText, new int[] { 0 });
    }

    public void generateImage(String password, File pdfFile, int resolution, File imageFile) throws IOException, InterruptedException {
        List<String> args = newGsArgs("jpeg", password, imageFile);
        args.add("-dTextAlphaBits=4");
        args.add("-dGraphicsAlphaBits=4");
        args.add("-r" + resolution);

        StringWriter returnedText = new StringWriter();
        execute(args, pdfFile, returnedText);
    }

    public static final int run(List<String> args, int secTimeOut, StringWriter returnedText, int... expectedExitValues) throws InterruptedException, IOException {
        LOGGER.info(args.toString());
        ProcessBuilder processBuilder = new ProcessBuilder(args);
        processBuilder.redirectErrorStream(true);
        Process process = processBuilder.start();
        if (!waitFor(process, secTimeOut)) {
            throw new InterruptedException("process execute timeout, abort.");
        }

        IOUtils.copy(process.getInputStream(), returnedText, StandardCharsets.UTF_8.toString());
        int exitValue = process.exitValue();
        LOGGER.info(returnedText.toString());
        LOGGER.info("exitValue: {}", Integer.valueOf(exitValue));

        if (expectedExitValues == null) {
            return exitValue;
        }

        boolean found = false;
        for (int expectedExitValue : expectedExitValues) {
            if (expectedExitValue == exitValue) {
                found = true;

                break;
            }
        }
        if (!found) {
            throw new IOException("wrong exit value, can't found exit value in expectedExitValues");
        }

        return exitValue;
    }

    public static boolean waitFor(Process process, int secTimeOut) throws InterruptedException {
        long last = System.currentTimeMillis() + (secTimeOut * 1000);
        while (true)
        {
            try { process.exitValue();
                return true; }
            catch (IllegalThreadStateException ex)
            { Thread.sleep(100L);

                if (System.currentTimeMillis() >= last)
                    break;  }  }  return false;
    }

    public static void main(String[] args) throws Exception {
        long start = System.currentTimeMillis();
        String pw = null;
        File pdfFile = new File("/home/irun/Desktop/ting.pdf");
        int resolution = 150;
        File imageFile = new File("/home/irun/Desktop/ting-%d.jpg");

        (new GsCmdUtil(null)).generateImage(pw, pdfFile, resolution, imageFile);
        System.out.println("elapsed time: " + (System.currentTimeMillis() - start));
    }
}

